<!DOCTYPE html>
<html lang="zh">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>日历选择组件</title>
    <style>
        *{padding: 0;margin: 0;}
        .field{
            width: 300px;
            margin: 20px auto;
            font-size: 12px;
            position: relative;
        }
        .field input{
            width: 100%;
            box-sizing: border-box;
            outline: none;
        }
        .date-select{
            background: #e8e8e8;
            color: #fff;
            cursor: pointer;
            transition: all .8s;
            position: absolute;
            width: 100%;
            z-index: 2;
            display: none;
        }

        .date-select table{
            width: 100%;
            border-collapse: collapse;
            text-align: center;
        }


        .date-select table caption:after{
            content: '';
            display: block;
            clear: both;
        }

        .date-select table caption{
            background: #999;
            line-height: 20px;
        }

        .date-select table th{
            background: #999;
        }

        .date-select table tr:first-child{
            line-height: 30px;
        }

        .date-select table tr td{
             border: 1px solid #f0f0f0;
        }

        .date-select table tr td.current-month-day{
            background: #d6d6d6;
        }

        .date-select table tr:not(:first-child){
            line-height: 25px;
        }

        .date-select table caption span#prev-month{
            float: left;
            padding-left: 10px;
        }
        .date-select table caption span#next-month{
            float: right;
            padding-right: 10px;
        }
    </style>
</head>
<body>
    <div class="field">
         <input type="text" value="" id="date-input" placeholder="请输入日期"/>
         <div class="date-select">
            <!--<table>
                <caption>
                    <span id="prev-month"> < </span>
                    <small>2017 - 3</small>
                    <span id="next-month"> > </span>
                    
                </caption>
                <tbody>
                    <tr>
                        <th>一</th>
                        <th>二</th>
                        <th>三</th>
                        <th>四</th>
                        <th>五</th>
                        <th>六</th>
                        <th>日</th>
                    </tr>

                    <tr>
                        <td>1</td>
                        <td>2</td>
                        <td>3</td>
                        <td>4</td>
                        <td>5</td>
                        <td>6</td>
                        <td>7</td>
                    </tr>
                </tbody>
            </table>-->
         </div>
    </div>

    <script>


        const dateInput = document.querySelector('#date-input');

        const dateSelect = document.querySelector('.date-select');

        dateInput.onclick = () => {
            if(dateSelect.style.display == "block" ){
                dateSelect.style.display = "none";
                return;
            }
            dateSelect.style.display = "block";
        }


        function get42ArrayDate(year, month){
            // month 是正常月份
            let now = year && month? new Date(year, month -1 ) : new Date();
            let _year = now.getFullYear();
            let _month = now.getMonth(); // Date 的月份比正常的月份小 1

            let firstDayDateObj = new Date(_year, _month, 1); // 当月第一天日期

            let _day = firstDayDateObj.getDay() == 0 ? 7 : firstDayDateObj.getDay(); // 修正一下星期的日期，当为 0 的时候说明为星期天

            let needFillDayCount = _day - 1; // 我们需要补的日期， 当我们一号为星期天的时候，也就是 _date 为 7，则我们需要把星期一到星期六的天数给补完

            let prevMonthLastDayDateObj = new Date(_year, _month, 0); // 上一个月最后一天的日期
            let currentMonthLastDayDateObj = new Date(_year, _month + 1, 0); // 当前月最后一天的日期

            let arr = Array.from({length:42}, (v, k) => k - needFillDayCount + 1 ) ; // 需要填充的数组 0 - 41 = 42 位  再加 1 是为了让 K 从 1开始 
            console.log(arr);
            let ret_arrObj = arr.map((val, index) => {
                // 现在我们对 负数日期，和超出限定的日期进行修正处理
                // 我们留下这个负数和超出的是为了让其他地方也可以使用这些数据通过 Date 直接构造正确的日期
                // 首先修正负数, 并且把正确的放在一个对象里面
                let prevMonthLastDay = prevMonthLastDayDateObj.getDate(); // 上个月最后一天的日期
                let currentMonthLastDay = currentMonthLastDayDateObj.getDate(); // 当前月最后一天
                let ret_obj = {
                    index: val
                };
                if( val <= 0 ){
                   // 对负数进行修正
                   ret_obj.showDate = prevMonthLastDay + val;
                   ret_obj.showMonth = _month; // showMonth 顾名思义应该为我们所理解的月份，也就是从1开始的，而 _month 是从 0 开始的，所以我们应该 + 1， 而这里是上个月的，所以要 -1 所以刚好抵消
                }else if (val > currentMonthLastDay){
                    // 对超出日期进行修正
                    ret_obj.showDate = val - currentMonthLastDay ;
                    ret_obj.showMonth = _month + 2;
                }else{
                    // 正常的本月日期
                    ret_obj.showDate = val;
                    ret_obj.showMonth = _month + 1;
                }

                return ret_obj;
            });

            return {
                currentMonth: _month + 1,
                currentYear: _year,
                dateArray: ret_arrObj
            }

        }

        function render(date){
            let code = `
            <table>
                <caption>
                    <span id="prev-month"> < </span>
                    <small>${date.currentYear} - ${date.currentMonth}</small>
                    <span id="next-month"> > </span>
                </caption>
                <tbody>
                    <tr>
                        <th>一</th>
                        <th>二</th>
                        <th>三</th>
                        <th>四</th>
                        <th>五</th>
                        <th>六</th>
                        <th>日</th>
                    </tr>
                    <tr>
            `;

            date.dateArray.forEach((item, index) => {
                if(index % 7 == 0) {
                    code += '</tr><tr>'
                }
                code += `<td class="${date.currentMonth == item.showMonth? 'current-month-day': ''}" data-index='${item.index}'>${item.showDate}</td>`
            });

            dateSelect.innerHTML = code;

           const prevMonthBtn = document.querySelector('#prev-month'),
                 nextMonthBtn = document.querySelector('#next-month');
                    
            prevMonthBtn.onclick = () => {
                let month = date.currentMonth - 1;
                let year = date.currentYear;
                let prevMonthDate = get42ArrayDate(year, month);
                render(prevMonthDate); // 重新渲染数据
            }

            nextMonthBtn.onclick = () => {
                let month = date.currentMonth + 1;
                let year = date.currentYear;
                let nextMonthDate = get42ArrayDate(year, month);
                render(nextMonthDate); // 重新渲染数据
            }

            dateSelect.addEventListener('click', (e) => {
                let targetDom = e.target;
                console.dir(targetDom)
                if(targetDom.tagName == 'SPAN' || targetDom.tagName != 'TD') return;
                let selectDay = new Date(date.currentYear, date.currentMonth - 1, targetDom.dataset.index); // currentMonth是正常月份所以要减一
                const inputValue = selectDay.getFullYear() + " - " + (selectDay.getMonth() + 1) + " - " + selectDay.getDate();
                dateInput.value = inputValue;
                dateSelect.style.display = "none"; // 隐藏选择框
            },false)


        }
        let date = get42ArrayDate();
        render(date);
        

    </script>
</body>
</html>